﻿using System;
using System.Configuration;
using System.IO;
using System.Net.Sockets;
using Polly;
using Polly.Retry;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Exceptions;

namespace Xms.MQ
{
    public class RabbitMQFactory
    {

        //static string host = ConfigurationManager.AppSettings["RabbitMQ_Host"].ToString();
        //static string userName = ConfigurationManager.AppSettings["RabbitMQ_UserName"].ToString();
        //static string password = ConfigurationManager.AppSettings["RabbitMQ_Password"].ToString();
        //static string port = ConfigurationManager.AppSettings["RabbitMQ_Port"].ToString();
        static string host = "127.0.0.1";
        static string userName = "guest";
        static string password = "guest";
        static string port = "5672";
        public readonly static IConnectionFactory _connectionFactory;
        IConnection _connection;
        object sync_root = new object();
        bool _disposed;
        static RabbitMQFactory()
        {
            {
                _connectionFactory = new ConnectionFactory() { HostName = host, UserName = userName, Password = password, Port = port == "" ? 5672 : int.Parse(port) };
            }
        }
        public bool IsConnected => this._connection != null && this._connection.IsOpen && this._disposed;
        public IModel CreateModel()
        {
            if (!this.IsConnected)
            {
                this.TryConnect();
            }
            return this._connection.CreateModel();
        }
        public bool TryConnect()
        {
            lock (this.sync_root)
            {
                RetryPolicy policy = RetryPolicy.Handle<SocketException>()
                    .Or<BrokerUnreachableException>()
                    .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)), (ex, time) =>
                    {

                    });
                policy.Execute(() =>
                {
                    this._connection = _connectionFactory.CreateConnection();

                });

                if (this.IsConnected)
                {
                    this._connection.ConnectionShutdown += this.OnConnectionShutdown;
                    this._connection.CallbackException += this.OnCallbackException;
                    this._connection.ConnectionBlocked += this.OnConnectionBlocked;
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }

        void OnConnectionShutdown(object sender, ShutdownEventArgs reason)
        {
            if (this._disposed) return;
            this.TryConnect();
        }
        /// <summary>
        ///   
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnCallbackException(object sender, CallbackExceptionEventArgs e)
        {
            if (this._disposed) return;
            this.TryConnect();
        }
        private void OnConnectionBlocked(object sender, ConnectionBlockedEventArgs e)
        {
            if (this._disposed) return;
            this.TryConnect();
        }

        public void Dispose()
        {
            if (this._disposed) return;

            this._disposed = true;

            try
            {
                this._connection.Dispose();
            }
            catch (IOException ex)
            {
            }
        }
    }
}
